from django.shortcuts import render, redirect, reverse
from django.contrib.auth import logout, login
from django.contrib.admin.views.decorators import staff_member_required
from django.views.generic import View
from django.views.decorators.http import require_POST, require_GET  # 限制视图函数只能用某一种请求
from apps.cms.models import NewsCategory
from utils import restful, constant
from .forms import EditNewsCategory
from django.db.utils import IntegrityError
import os, oss2, json, base64, mimetypes
from django.conf import settings
from oss2 import SizedFileAdapter, determine_part_size
from oss2.models import PartInfo
from urllib.parse import quote


# from libmagic import magic


# Create your views here.

# 权限装饰器(django内置的admin系统在中的), 如果没有权限的话则调回login_url指定的地址
@staff_member_required(login_url='/')
def index(request):
	return render(request, 'cms/index.html')


def logout_view(request):
	logout(request)
	return redirect(reverse('news:index'))


# 因为既有get请求也有post请求, 所以可以用类视图
class WriteNewsView(View):
	def get(self, request):
		categories = NewsCategory.objects.filter(sys_flag=1).order_by('-pk')  # order_by('id')是一样
		context = {
			'categories': categories,
		}
		return render(request, 'cms/write_news.html', context=context)

	def post(self, request):
		pass


def new_category(request):
	categories = NewsCategory.objects.filter(sys_flag=1).order_by('-pk')  # order_by('id')是一样
	context = {
		'categories': categories,
	}
	return render(request, 'cms/news_category.html', context=context)


@require_POST
def add_new_category(request):
	name = request.POST.get('name')
	exists = NewsCategory.objects.filter(name=name).exists()
	if not exists:
		NewsCategory.objects.create(name=name)
		return restful.ok()
	else:
		return restful.params_error(message='该分类已经存在!')


@require_POST
def edit_category(request):
	form = EditNewsCategory(request.POST)
	if form.is_valid():
		pk = form.cleaned_data.get('pk')
		name = form.cleaned_data.get('name')
		try:
			NewsCategory.objects.filter(pk=pk, sys_flag=1).update(name=name)
			return restful.ok()
		except IntegrityError:
			return restful.params_error(message='该分类已存在!')
		except:
			return restful.params_error(message='该分类不存在!')
	else:
		return restful.params_error(message=form.get_errors())


@require_POST
def delete_category(request):
	pk = request.POST.get('pk')
	try:
		NewsCategory.objects.filter(pk=pk).update(sys_flag=0)
		return restful.ok()
	except:
		return restful.params_error(message='该分类不存在!')


@require_POST
def upload_file(request):
	file_obj = request.FILES.get('file')
	# file_obj.save()  # 只适用于小文件的上传, 大文件上传容易导致内存移除
	if not file_obj:
		return restful.params_error(message='没有上传任何文件!')
	name = file_obj.name  # xx.jpg
	file_path = os.path.join(settings.MEDIA_ROOT, name)
	with open(file_path, 'wb') as f:
		for chunk in file_obj.chunks():  # chunks 在 django.core.files.uploadedfile.InMemoryUploadedFile中
			f.write(chunk)
		f.close()
		# settings.MEDIA_URL + name   /media/0.jpg
		# http://127.0.0.1:8000/media/0.jpg
		url = request.build_absolute_uri(settings.MEDIA_URL + name)
	return restful.result(data={'url': url})


# 阿里云对象存储分片上传方式
@require_POST
def aliOOS(request):
	# 阿里云主账号AccessKey拥有所有API的访问权限，风险很高。强烈建议您创建并使用RAM账号进行API访问或日常运维，请登录 https://ram.console.aliyun.com 创建RAM账号。
	# accessKeyIdLTA = 'IhIhy1KINY4ac'
	# accessKeySecretSYS = 'TC4iaFbY9a1vHtDR0XSKRJdHLhr'
	auth = oss2.Auth(constant.accessKeyId, constant.accessKeySecret)
	# Endpoint以杭州为例，其它Region请按实际情况填写。
	bucket_name = 'tinxi-hb-01'
	bucket = oss2.Bucket(auth, 'http://oss-cn-qingdao.aliyuncs.com', bucket_name)

	file_obj = request.FILES.get('file')  # 获取本地上传的文件
	# file_obj.save()  # 只适用于小文件的上传, 大文件上传容易导致内存移除
	if not file_obj:
		return restful.params_error(message='没有上传任何文件!')
	key = file_obj.name  # 上传后的文件名
	filename = file_obj.name  # 本地待上传的文件名 xx.jpg
	file_path = os.path.join(settings.MEDIA_ROOT, file_obj.name)

	total_size = os.path.getsize(file_path)
	# determine_part_size方法用来确定分片大小。
	part_size = determine_part_size(total_size, preferred_size=100 * 1024)

	# 检查文件类型
	# mime_type = magic.from_file(full_path, mime=True)
	# logger.info("上传的文件类型：" + str(mime_type))
	# if not mime_type.startswith('video') and not mime_type.startswith('image'):
	# 	logger.error("非法的文件类型!")
	# 	os.remove(full_path)
	# 	return JsonResponse({'code': 500, 'msg': '非法的文件类型！'})
	# mimetypes.types_map(file_path)

	# 初始化分片。
	upload_id = bucket.init_multipart_upload(key).upload_id
	parts = []

	# 逐个上传分片。
	with open(file_path, 'rb') as fileobj:
		part_number = 1
		offset = 0
		while offset < total_size:
			num_to_upload = min(part_size, total_size - offset)
			# SizedFileAdapter(fileobj, size)方法会生成一个新的文件对象，重新计算起始偏移量。
			result = bucket.upload_part(key, upload_id, part_number,
										SizedFileAdapter(fileobj, num_to_upload))
			parts.append(PartInfo(part_number, result.etag))
			offset += num_to_upload
			part_number += 1
	# 完成分片上传。
	bucket.complete_multipart_upload(key, upload_id, parts)

	# 验证分片上传。
	with open(file_path, 'rb') as fileobj:
		assert bucket.get_object(key).read() == fileobj.read()

	'''
	# 准备回调参数。
	# callback_dict = {}
	url_decode = 'https://tinxi-hb-01.oss-cn-qingdao.aliyuncs.com/' + quote(key, encoding='utf-8')
	# callback_dict['callbackUrl'] = url_decode
	# # callback_dict['callbackHost'] = 'oss-cn-hangzhou.aliyuncs.com'
	# mimeType = 'image/png'
	# callback_dict['callbackBody'] = 'filename=${key}&size=${total_size}&mimeType=${mimeType}'
	# callback_dict['callbackBodyType'] = 'application/x-www-form-urlencoded'
	# 回调参数是Json格式，并且需要Base64编码。
	callback_dict = {
		'callbackUrl': url_decode,
		'callbackBody': 'filename=${key}&size=${total_size}&mimeType=${mimeType}'
		# 'callbackBodyType': 'application/json'
	}
	callback_param = json.dumps(callback_dict).strip()

	base64_callback_body = base64.b64encode(callback_param.encode())

	# 回调参数编码后放在Header中发送给OSS。
	headers = {'x-oss-callback': base64_callback_body.decode()}
	# print(type(headers))
	# 上传并回调。
	print(type(key))
	result = bucket.put_object(key, 'a' * 1024 * 1024, headers)
	print(result)
	'''
	url = 'https://tinxi-hb-01.oss-cn-qingdao.aliyuncs.com/' + quote(key, encoding='utf-8')
	return restful.result(data={'url': url})
